﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Lm.Common.Threading
{
    /// <summary>
    /// 此类以线程池为基础，进行多线程管理。
    /// 如果要调整最大、最小线程数量，请直接对线程池（ThreadPool）进行设置。
    /// 与线程池不同的是，将任务排队到线程池的时候，多了一个参数，workItem。
    /// 以workItem的ItemType和ItemKey生成一个字符串类型的UniqueKey，用来标识此任务。
    /// 具有相同UniqueKey的任务，具有互斥性，一个完成后，才会执行下一个。
    /// 具有不同UniqueKey的任务，并发执行。
    /// </summary>
    public class ThreadPoolWithLock
    {
        protected Dictionary<string, LockItem> lockList = new Dictionary<string, LockItem>();

        protected EventWaitHandle GetLock(WorkItem workItem)
        {
            lock (this.lockList)
            {
                if (this.lockList.Keys.Contains(workItem.UniqueKey))
                { 
                    var lockItem = this.lockList[workItem.UniqueKey];
                    lockItem.Count++;
                    return lockItem.Lock;
                }
                else
                {
                    EventWaitHandle ewh = new EventWaitHandle(true, EventResetMode.AutoReset);
                    var lockItem = new LockItem() { Count = 1, Lock = ewh };
                    this.lockList.Add(workItem.UniqueKey, lockItem);
                    return ewh;
                }
            }
        }

        protected void ReleaseLock(WorkItem workItem)
        {
            lock (this.lockList)
            {
                var lockItem = this.lockList[workItem.UniqueKey];
                if (lockItem.Count == 1)
                { this.lockList.Remove(workItem.UniqueKey); }
                else
                { lockItem.Count--; }              
            }
        }

        public bool QueueUserWorkItem(WaitCallback callBack, WorkItem workItem)
        {
            return ThreadPool.QueueUserWorkItem((obj) =>
              {
                  var ewh = this.GetLock(workItem);
                  ewh.WaitOne();
                  Console.WriteLine("start:"+workItem.ItemKey);
                  callBack(null);
                  Console.WriteLine("end:" + workItem.ItemKey);
                  ewh.Set();
                  this.ReleaseLock(workItem);
              });
        }

        public bool QueueUserWorkItem(WaitCallback callBack, Object state, WorkItem workItem)
        {
            return ThreadPool.QueueUserWorkItem((obj) =>
            {
                var ewh = this.GetLock(workItem);
                ewh.WaitOne();
                Console.WriteLine("start:" + workItem.ItemKey);
                Console.WriteLine(state.ToString());
                callBack(state);
                Console.WriteLine("end:" + workItem.ItemKey);
                ewh.Set();
                this.ReleaseLock(workItem);
            });
        }     
    }

    public class WorkItem
    {
        public string ItemType { get; set; }

        public string ItemKey { get; set; }

        public string UniqueKey
        {
            get
            {
                return this.ItemType + "{0F1D2640-F984-425C-8A99-9EA1E4680AE2}" + this.ItemKey;
            }
        }
    }

    public class LockItem
    {
        public EventWaitHandle Lock { get; set; }

        /// <summary>
        /// 多少条线程在使用此锁
        /// </summary>
        public int Count { get; set; }
    }
}